/* Copyright (C) 2010 Moritz Bitsch
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 *      Moritz Bitsch <moritzbitsch@googlemail.com>
 */

namespace Webappi {
	public class Request {
		public HashTable<string,string> param;
		public HashTable<string,TempFile> upload;
		private scgi.Request req;

		public Request(scgi.Request req) {
			param = new HashTable<string,string>.full(str_hash,str_equal, g_free, g_free);
			upload = new HashTable<string,TempFile>.full(str_hash,str_equal,g_free, TempFile.unref);

			this.req = req;

			process_form();
		}

		/*
		* Parse a query-string and extract key-value pairs as HashTable
		*/
		private HashTable<string,string> process_data(string data, string delim, string sep) {
			var result = new HashTable<string,string>.full(str_hash,str_equal, g_free, g_free);

			foreach (string pair in data.split(sep)) {
				var kv = pair.split(delim);
				if (kv.length == 2) {
					var value = Uri.unescape_string(kv[1]).chomp();
					if (value[0] == '"' && value[value.length-1] == '"') {
						value = value[1:value.length-1];
					}
					result.insert(Uri.unescape_string(kv[0]).chomp(), value);
				}
			}
			return result;
		}

		/*
		 * read_line returning a StringBuilder, usable for binary data
		 */
		private StringBuilder? read_line(FileStream stream) {
			int c;
			StringBuilder? ret = null;
			var istream = new DataInputStream(this.req.input);
			while ((c = istream.read_byte(null)) != 0) {
				if (ret == null) {
					ret = new StringBuilder ();
				}
				if (c == '\n') {
					break;
				}
				ret.append_c ((char) c);
			}
			if (ret == null) {
				return null;
			} else {
				return ret;
			}
		}

		/*
		* Parse a GET or POST request, extract vars using process_data
		*/
		private void process_form() {
			var method = this.req.params.lookup("REQUEST_METHOD");
			switch (method) {
				case "GET":
					var qdata = this.req.params.lookup("QUERY_STRING");
					this.param = process_data(qdata, "=", "&");
					break;
				case "POST":
					var content_length = this.req.params.lookup("CONTENT_LENGTH").to_int();
					var content_type = this.req.params.lookup("CONTENT_TYPE");
					if (content_type.down().has_prefix("multipart/")) {
						this.param = new HashTable<string,string>(str_hash,str_equal);

						var boundary = process_data(content_type, "=", "; ").lookup("boundary");
						var seperator = "--" + boundary;
						var end = seperator + "--";

						StringBuilder line;
						bool partHeader = false;

						string fieldName = "";
						string fileName = "";
						bool isFile = false;
						TempFile tmpFile = null;

						while ((line = read_line(stdin)) != null ) {
							//Disable first
							if (partHeader && line.str == "\r") {
								partHeader = false;
								continue;
							}

							//We are parsing a mime header
							if (partHeader) {
								//FIXME: Need some temp storage here
								if (line.str.down().has_prefix("content-disposition")) {
									var last = line.str.str(";");
									var options = process_data(last, "=", "; ");
									fieldName = options.lookup("name");
									fileName = options.lookup("filename");
									if (fileName != null) {
										//It is a file upload
										isFile = true;
										tmpFile = new TempFile.with_template("webappiXXXXXX");
									}
								}
								if (line.str.down().has_prefix("content-type")) {
									var ctype = line.str.str(": ")[2:line.str.length];
								}
								continue;
							}

							//Enable last
							if (line.str.has_prefix(seperator) || line.str.has_prefix(end)) {
								if (isFile) {
									this.upload.insert(fieldName, tmpFile);
									tmpFile.flush();
									tmpFile.rewind();
									dprint(LogLevel.Insane, "Request", "saved file here: %s".printf(tmpFile.filename));
								}
								tmpFile = null;
								partHeader = true;
								isFile = false;
								fieldName = "";
								fileName = "";

								if (line.str.has_prefix(end)) {
									break;
								}
								continue;
							}

							if (isFile) {
								for (int i = 0; i<line.len; i++) {
									tmpFile.putc((char)line.str[i]);
								}
								tmpFile.putc('\n');
							} else {
								this.param.insert(fieldName, line.str);
							}
						}
					} else {
						var buffer = new StringBuilder.sized(content_length);
						int c;
						var istream = new DataInputStream(this.req.input);
						while ((c = istream.read_byte(null)) != 0) {
							buffer.append_c ((char) c);
						}
						this.param = process_data(buffer.str, "=", "&");
					}
					break;
				default:
					//FIXME: what to do here...
					break;
			}
		}
	}
}
